//@author A0081229H
package storage;

import java.util.ArrayList;

public class Time implements Comparable<Time>, Clonable<Time>, Observable, DateTime{

	private static final int HOUR_FIRST = 0;
	private static final int HOUR_LAST = 23;
	private static final int MINUTE_FIRST = 0;
	private static final int MINUTE_LAST = 59;
	private static final int SECOND_FIRST = 0;
	private static final int SECOND_LAST = 59;

	private static final String SETHOUR = "setHour";
	private static final String SETMINUTE = "setMinute";
	private static final String SETSECOND = "setSecond";

	private static final String ERROR_MESSAGE_INVALID_HOUR = "Invalid Hour";
	private static final String ERROR_MESSAGE_INVALID_MINUTE = "Invalid Minute";
	private static final String ERROR_MESSAGE_INVALID_SECOND = "Invalid Second";

	private Integer hour, minute, second;

	private ArrayList<Observer> observerList;

	// Constructors

	public Time() {
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		hour = null;
		minute = null;
		second = null;
		observerList = new ArrayList<Observer>();
	}

	public Time(Integer pHour) throws Exception {
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		if (isHourValid(pHour)) {
			hour = pHour;
		} else {
			throw new Exception(ERROR_MESSAGE_INVALID_HOUR);
		}
		minute = null;
		second = null;
		observerList = new ArrayList<Observer>();
	}

	public Time(Integer pHour, Integer pMinute) throws Exception{
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		if (isHourValid(pHour)) {
			hour = pHour;
		} else {
			throw new Exception(ERROR_MESSAGE_INVALID_HOUR);
		}
		if (isMinuteValid(pMinute)) {
			minute = pMinute;
		} else {
			throw new Exception(ERROR_MESSAGE_INVALID_MINUTE);
		}
		observerList = new ArrayList<Observer>();
	}

	public Time(Integer pHour, Integer pMinute, Integer pSecond) throws Exception {
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		if (isHourValid(pHour)) {
			hour = pHour;
		} else {
			throw new Exception(ERROR_MESSAGE_INVALID_HOUR);
		}
		if (isMinuteValid(pMinute)) {
			minute = pMinute;
		} else {
			throw new Exception(ERROR_MESSAGE_INVALID_MINUTE);
		}
		if (isSecondValid(pSecond)) {
			second = pSecond;
		} else {
			throw new Exception(ERROR_MESSAGE_INVALID_SECOND);
		}
		observerList = new ArrayList<Observer>();
	}

	// Access Methods

	public Integer getHour() {
		return hour;
	}

	public Integer getMinute() {
		return minute;
	}

	public Integer getSecond() {
		return second;
	}

	// Verification Methods

	public boolean isEmpty() {
		if (hour == null &&
				minute == null &&
				second == null) {
			return true;
		} else {
			return false;
		}
	}

	private boolean isHourValid(Integer pHour) {
		if (pHour >= HOUR_FIRST &&
				pHour <= HOUR_LAST) {
			return true;
		} else {
			return false;
		}
	}

	private boolean isMinuteValid(Integer pMinute) {
		if (pMinute >= MINUTE_FIRST &&
				pMinute <= MINUTE_LAST) {
			return true;
		} else {
			return false;
		}
	}

	private boolean isSecondValid(Integer pSecond) {
		if (pSecond >= SECOND_FIRST &&
				pSecond <= SECOND_LAST) {
			return true;
		} else {
			return false;
		}
	}

	// Modification Methods

	public boolean setHour(Integer pHour) throws Exception {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETHOUR + Helper.SPACE + pHour);
		} catch (Exception e) {

		}
		if (isHourValid(pHour)) {
			hour = pHour;
			notifyObs();
			return true;
		} else {
			return false;
		}
	}

	public boolean setMinute(Integer pMinute) {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETMINUTE + Helper.SPACE + pMinute);
		} catch (Exception e) {

		}
		if (isMinuteValid(pMinute)) {
			minute = pMinute;
			notifyObs();
			return true;
		} else {
			return false;
		}
	}

	public boolean setSecond(Integer pSecond) {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETSECOND + Helper.SPACE + pSecond);
		} catch (Exception e) {

		}
		if (isSecondValid(pSecond)) {
			second = pSecond;
			notifyObs();
			return true;
		} else {
			return false;
		}
	}

	// Interfaces

	@Override
	public int compareTo(Time pTime) {
		return Helper.compareToCustom(getHour(), pTime.getHour(),
				getMinute(), pTime.getMinute(),
				getSecond(), pTime.getSecond());
	}

	@Override
	public Time cloneSelf() throws Exception {
		Time clone;
		if (isEmpty() || getHour() == null) {
			clone = new Time();
		} else if (getMinute() == null) {
			clone = new Time(getHour());
		} else if (getSecond() == null) {
			clone = new Time(getHour(), getMinute());
		} else {
			clone = new Time(getHour(), getMinute(), getSecond());
		}
		return clone;
	}


	@Override
	public void notifyObs() {
		for (Observer o : observerList) {
			o.update();
		}
	}

	@Override
	public void addObs(Observer o) {
		observerList.add(o);		
	}

	@Override
	public Integer getFirstAttribute() {
		return getHour();
	}

	@Override
	public Integer getSecondAttribute() {
		return getMinute();
	}

	@Override
	public Integer getThirdAttribute() {
		return getSecond();
	}

	@Override
	public boolean equals(Object obj) {
		if(obj instanceof Time) {
			Time time = (Time) obj;
			return this.hour.equals(time.hour)
					&& this.minute.equals(time.minute) && (this.second != null)?this.second.equals(time.second):true;
		}
		else {
			return false;
		}
	}
}
